package com.xcity.game.worldboss.combat;

import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

import com.xcity.db.entity.Player;
import com.xcity.game.combat.Combat;
import com.xcity.game.combat.Combat.State;
import com.xcity.game.combat.Combat.Type;
import com.xcity.game.combat.CombatException;
import com.xcity.game.combat.CombatService;
import com.xcity.game.combat.Round;
import com.xcity.game.combat.attr.CombatAttributeType;
import com.xcity.game.combat.builder.DefaultCombatBuilder;
import com.xcity.game.combat.impl.TimeLimitedCombat;
import com.xcity.game.combat.listener.CombatListener;
import com.xcity.game.combat.unit.CombatUnit;
import com.xcity.game.combat.unit.impl.NpcCombatUnit;
import com.xcity.game.common.Cause;
import com.xcity.game.common.MessageConst;
import com.xcity.game.event.GameEvents;
import com.xcity.game.formation.Formation;
import com.xcity.game.item.ItemInfo;
import com.xcity.game.item.ItemService;
import com.xcity.game.item.drop.Drop;
import com.xcity.game.item.drop.listener.MultiDropAdapter;
import com.xcity.game.npc.NpcCombatable;
import com.xcity.game.npc.NpcTeamTemplate;
import com.xcity.game.npc.NpcTemplate;
import com.xcity.game.worldboss.WorldBossUtils;
import com.xcity.game.worldboss.model.WorldBoss;
import com.xcity.game.worldboss.template.WorldBossConfig;
import com.xcity.game.worldboss.template.WorldBossTemplate;
import com.xcity.pomelo.push.msg.CombatEndPush;
import com.xcity.pomelo.request.CombatInfoRequest;
import com.xcity.pomelo.request.CombatWorldBossRequest;
import com.xcity.pomelo.response.CombatInfoResponse;
import com.xcity.pomelo.response.CombatInfoResponse.TeamBuilder;
import com.xcity.pomelo.response.CombatResponse;

import naga.x.App;
import naga.x.net.Packet;
import naga.x.net.Session;

public class WorldBossCombatListener implements CombatListener {

	private Player player;
	private WorldBossTemplate template;
	private int[] npcInitHps;
	
	private WorldBossCombatInfo info;
	
	public static final int COMBAT_DELAY_MILLIS = 3000; // 战斗限时延长3秒(客户端有3秒准备阶段)
	
	@Override
	public void roundStart(Round round) {
	}

	@Override
	public void roundEnd(Round round) {
	}
	
	private void validate(Player player) throws CombatException {
		if (WorldBossUtils.getLeftTimes(player) < 1) {
			throw new CombatException(MessageConst.WORLD_BOSS_HAS_NO_JOIN_TIMES);
		}
		this.player = player;
		WorldBoss boss = WorldBossUtils.getBoss();
		this.template = boss.getTemplate();
		int[] hps = boss.getHps();
		this.npcInitHps = new int[hps.length];
		System.arraycopy(hps, 0, this.npcInitHps, 0, hps.length);
	}
	
	private boolean isNpcDied(int index) {
		return npcInitHps[index] <= 0;
	}

	@Override
	public void prepareCombat(Session session, CombatInfoRequest packet) throws CombatException {
		Player player = session.getClient();
		validate(player);
		TeamBuilder srcBuilder = new TeamBuilder()
				.setLeader(player)
				.setMembers(Arrays.asList(player.getFormations().get(Formation.TYPE_NORMAL).getUnits()));
		TeamBuilder dstBuilder = new TeamBuilder();
		NpcTeamTemplate npcTeam = template.getNpcTeam();
		if (!isNpcDied(0)) {
			dstBuilder.setLeader(toCombatable(npcTeam.getBoss(), npcInitHps[0]));
		}
		NpcTemplate[] npcs = npcTeam.getNpcs();
		for (int i = 0, len = npcs != null ? npcs.length : 0; i < len; i++) {
			dstBuilder.addMember(toCombatable(npcs[i], npcInitHps[i + 1]));
		}
		CombatInfoResponse res = new CombatInfoResponse(packet.getSerial(), srcBuilder.build(), dstBuilder.build(), Formation.TYPE_NORMAL, WorldBossConfig.combatTimeLimit);
		session.send(res);
	}
	
	private static NpcCombatable toCombatable(NpcTemplate npc, int hp) {
		if (hp > 0) {
			NpcCombatable c = new NpcCombatable(npc);
			c.setAttributeValue(CombatAttributeType.HP, hp);
			return c;
		}
		return null;
	}

	@Override
	public CombatResponse create(CombatService service, Type type, Session session, Packet packet) throws CombatException {
		CombatWorldBossRequest req = (CombatWorldBossRequest) packet;
		Player player = session.getClient();
		validate(player);
		if (!WorldBossUtils.tryJoin(player)) {
			throw new CombatException(MessageConst.WORLD_BOSS_HAS_NO_JOIN_TIMES);
		}
		DefaultCombatBuilder builder = new DefaultCombatBuilder()
				.setType(type)
				.setListener(this)
				.setSourceLeader(player)
				.addSourceMembers(Arrays.asList(player.getFormations().get(Formation.TYPE_NORMAL).getUnits()));
		NpcTeamTemplate npcTeam = template.getNpcTeam();
		NpcTemplate boss = npcTeam.getBoss();
		if (boss != null && !isNpcDied(0)) {
			CombatUnit unit = new NpcCombatUnit(builder.getDestTeam(), boss, true);
			unit.setAttributeValue(CombatAttributeType.HP, npcInitHps[0]);
			builder.setDestLeader(unit);
		}
		NpcTemplate[] npcs = npcTeam.getNpcs();
		for (int i = 0, len = npcs != null ? npcs.length : 0; i < len; i++) {
			if (isNpcDied(i + 1)) {
				builder.addDestMember(null);
			} else {
				CombatUnit unit = new NpcCombatUnit(builder.getDestTeam(), npcs[i]);
				unit.setAttributeValue(CombatAttributeType.HP, npcInitHps[i + 1]);
				builder.addDestMember(unit);
			}
		}
		TimeLimitedCombat combat = (TimeLimitedCombat) builder.build(TimeLimitedCombat.class);
		combat.setTimeout(WorldBossConfig.combatTimeLimit * 1000 + COMBAT_DELAY_MILLIS);
		info = new WorldBossCombatInfo(player, template, null);
		App.getApp().getEventManager().addEvent(GameEvents.EVENT_WORLD_BOSS_COMBAT_BEGIN, player, info);
		CombatService.LOG.info("[WB BEGIN]ID[{}]BOSS[{}]", player.getId(), template.getLevel());
		return new CombatResponse(req.getSerial(), combat, WorldBossConfig.combatTimeLimit);
	}

	@Override
	public void end(Combat combat, boolean notify) {
		Result result = new Result();
		// 计算伤害
		int damage = WorldBossUtils.calcDamage(combat);
		result.scoreAdded = damage / WorldBossConfig.scoreRatio;
		result.score = WorldBossUtils.addScore(player, result.scoreAdded);
		// 奖励
		final Map<Integer, ItemInfo> itemsMapping = new LinkedHashMap<Integer, ItemInfo>();
		Drop drop = template.getDrop().getDrop();
		drop.drop(Drop.RND, player, Cause.WORLD_BOSS_COMBAT, new MultiDropAdapter() {
			@Override
			public void goldDroped(Player player, int gold) {
				itemDroped(player, ItemService.goldDefault.getId(), gold);
			}
			@Override
			public void stoneDroped(Player player, int stone) {
				itemDroped(player, ItemService.stoneDefault.getId(), stone);
			}
			@Override
			public void itemDroped(Player player, int itemId, int count) {
				Integer key = Integer.valueOf(itemId);
				ItemInfo item = itemsMapping.get(key);
				if (item == null) {
					item = new ItemInfo(itemId, 0);
					itemsMapping.put(key, item);
				}
				item.setCount(item.getCount() + count);
			}
		});
		result.rewards = new Reward();
		result.rewards.items = itemsMapping.values();
		
		byte succ = combat.getState() == State.SOURCE_WON ? Combat.SUCC : Combat.FAIL;
		if (notify) {
			CombatEndPush sync = new CombatEndPush(succ, Combat.Type.WORLD_BOSS, combat.getId(), result);
			player.send(sync);
		}
		info.npcHpDelta = WorldBossUtils.calcNpcHpDelta(combat, npcInitHps);
		App.getApp().getEventManager().fireEvent(GameEvents.EVENT_WORLD_BOSS_COMBAT_END, player, info, succ);
		CombatService.LOG.info("[WB END]ID[{}]BOSS[{}]DAMAGE[{}]RESULT[{}]", player.getId(), template.getLevel(), damage, succ);
	}
	
	public static class Result {
		public int scoreAdded; // 本次获得积分
		public int score; // 总积分
		public Reward rewards;
	}
	
	public static class Reward {
		public int gold;
		public int stone;
		public Collection<ItemInfo> items;
	}
	
}
